package com.runewbie.linkedlist.singlelinkedlist;

/**
 * @description: 使用带head头的单向链表实现 –水浒英雄排行榜管理 
 * 				 1、完成对英雄人物的增删改查操作， 注: 删除和修改,查找
 *               2、第一种方法在添加英雄时，直接添加到链表的尾部
 *               3、第二种方式在添加英雄时，根据排名将英雄插入到指定位置(如果有这个排名，则添加失败，并给出提示)
 * @author: runewbie
 * @date: 2020年6月7日 下午12:01:06
 * @version V6.0（【腾讯面试题，有点难度】单链表的反转）
 *
 */
public class SingleLinkedListDemo6 {

	// 测试
	public static void main(String[] args) {
		// 创建节点
		HeroNode6 hero1 = new HeroNode6(1, "宋江", "呼保义");
		HeroNode6 hero2 = new HeroNode6(2, "卢俊义", "玉麒麟");
		HeroNode6 hero3 = new HeroNode6(3, "吴用", "智多星");
		HeroNode6 hero4 = new HeroNode6(4, "公孙胜", "入云龙");

		// 创建链表
		SingleLinkedList6 singleLinkedList6 = new SingleLinkedList6();
		
		// 测试按照编号顺序添加节点
		singleLinkedList6.addByOrder(hero1);
		singleLinkedList6.addByOrder(hero2);
		singleLinkedList6.addByOrder(hero3);
		singleLinkedList6.addByOrder(hero4);

		// 遍历显示
		singleLinkedList6.list();
		
		//测试链表反转功能
		System.out.println("反转后链表的情况：");
		reverseList(singleLinkedList6.getHead());
		// 遍历显示
		singleLinkedList6.list();

	}

	// 将单链表反转
	public static void reverseList(HeroNode6 head) {
		// 如果当前链表为空，或者只有一个节点，无需反转，直接返回
		if (head.next == null || head.next.next == null) {
			return;
		}

		// 定义一个辅助的指针（变量）,帮助遍历原来的链表
		HeroNode6 cur = head.next; // 当前节点
		HeroNode6 next = null;// 指向当前节点【cur】的下一个节点
		HeroNode6 reverseHead = new HeroNode6(0, null, null);
		// 遍历原来的链表，每遍历一个节点，就将其取出，并放在新的链表 reverseHead 的前端
		while (cur != null) {
			next = cur.next;// 先暂时保存当前节点的下一个节点，因为后面需要用到
			cur.next = reverseHead.next;// 将cur的下一个节点指向新的链表的最前端
			reverseHead.next = cur;// 将cur连接到新的链表上
			cur = next;// 让cur指针后移
		}
		// 将head.next 指向 reverseHead.next，实现链表的反转
		head.next = reverseHead.next;
	}
	
}

// 定义SingleLinkedList管理水浒英雄
class SingleLinkedList6 {
	// 先初始化一个头结点，头结点不动，不存放具体的数据
	private HeroNode6 head = new HeroNode6(0, null, null);

	public HeroNode6 getHead() {
		return head;
	}

	// 添加节点到单向链表
	// 思路：当不考虑编号的顺序时
	// 1、找到当前链表的最后节点
	// 2、将最后这个节点的next指向新的节点
	public void add(HeroNode6 HeroNode6) {
		// 因为head节点不能动，因此我们需要一个辅助遍历 temp
		HeroNode6 temp = head;
		// 遍历链表，找到最后
		while (true) {
			// 找到链表的最后
			if (temp.next == null) {
				break;
			}
			// 如果没有找到最后，将temp后移
			temp = temp.next;
		}
		// 当退出while循环时，temp指向了链表的最后
		// 将最后这个节点的next指向新的节点
		temp.next = HeroNode6;
	}

	// 第二种方式在添加英雄的时候，根据排名将英雄插入到指定位置
	// (如果有这个排名，则添加失败，并给出提示)
	public void addByOrder(HeroNode6 HeroNode6) {
		// 因为头节点不能动，所以仍然通过一个辅助指针（变量）来帮助找到添加的位置
		// 因为是单链表，因此找的temp是位于添加位置的前一个节点，否则插入不了
		HeroNode6 temp = head;
		boolean flag = false;// flag标识添加的编号是否存在，默认为false
		
		while (true) {
			if (temp.next == null) {// 说明temp已经在链表的最后
				break;
			}
			if (temp.next.no > HeroNode6.no) {// 位置找到，就在temp的后面插入
				break;
			} else if (temp.next.no == HeroNode6.no) {// 说明希望添加的heerNode的编号已经存在
				flag = true;// 说明编号已经存在
				break;
			}
			
			temp = temp.next;// 后移，遍历当前链表
		}
		
		// 判断flag的值
		if (flag) {
			System.out.printf("准备插入的英雄的编号 %d 已经存在了，不能加入\n", HeroNode6.no);
		} else {
			// 插入到链表中，放在temp的后面
			HeroNode6.next = temp.next;
			temp.next = HeroNode6;
		}
	}
	
	// 修改节点信息，根据no编号来修改，即no编号不能改
	// 说明：
	// 根据heroNode3 的 no 来修改即可
	public void update(HeroNode6 HeroNode6) {
		// 判断是否为空
		if (head.next == null) {
			System.out.println("链表为空~~");
			return;
		}
		// 找到需要修改的节点，根据no编号
		// 定义一个辅助变量
		HeroNode6 temp = head.next;
		boolean flag = false;// 表示是否找到该节点
		while (true) {
			if (temp == null) {
				break;// 链表已经遍历完
			}
			if (temp.no == HeroNode6.no) {
				// 找到了
				flag = true;
				break;
			}
			// 指针先向后移动
			temp = temp.next;
		}
		// 根据flag判断是否找到要修改的节点
		if (flag) {
			temp.name = HeroNode6.name;
			temp.nickName = HeroNode6.nickName;
		} else {
			System.out.printf("没有找到编号为%d的节点，不能修改\n", HeroNode6.no);
		}
	}
	
	//删除节点
	//思路：
	//1、head不能动，因此我们需要一个temp辅助节点找到待删除节点的前一个节点
	//2、说明在比较时，是temp.next.no 和需要删除的节点的no比较
	public void del(int no) {
		HeroNode6 temp = head;
		boolean flag = false;// 标志是否找到待删除的节点
		while (true) {
			if (temp.next == null) {// 已经到链表的最后
				break;
			}
			if (temp.next.no == no) {
				// 找到待删除节点的点一个节点temp
				flag = true;
				break;
			}
			temp = temp.next;// temp后移到下一个节点
		}
		// 判断flag
		if (flag) {// 如果找到了
			// 可以删除该节点
			temp.next = temp.next.next;
		} else {
			System.out.printf("要删除的节点 %d 不存在\n", no);
		}
	}

	// 显示链表[遍历]
	public void list() {
		// 判断链表是否为空
		if (head.next == null) {
			System.out.println("链表为空");
			return;
		}
		// 因为头节点不能动，因此需要一个辅助变量来进行遍历
		HeroNode6 temp = head.next;
		while (true) {
			// 判断是否到了链表最后
			if (temp == null) {
				break;
			}
			// 输出节点的信息
			System.out.println(temp);
			// * 将temp后移，一定要小心，这里要是不后移的话，会导致死循环
			temp = temp.next;
		}
	}

}

//定义HeroNode，每个 HeroNode对象就是一个节点
class HeroNode6 {
	public int no;
	public String name;
	public String nickName;
	public HeroNode6 next;// 指向下一个节点
	// 构造器

	public HeroNode6(int no, String name, String nickName) {
		this.no = no;
		this.name = name;
		this.nickName = nickName;
	}

	// 为了显示方便，我们重写toString
	@Override
	public String toString() {
		return "HeroNode6 [no=" + no + ", name=" + name + ", nickName=" + nickName + "]";
	}
}